# Copyright 2024 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.

load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")
load("@bazel_skylib//rules:native_binary.bzl", "native_binary")
load("@bazel_skylib//rules:write_file.bzl", "write_file")
load("@rules_python//python:defs.bzl", "py_library")
load("@rules_python//python/entry_points:py_console_script_binary.bzl", "py_console_script_binary")
load("//pw_build:pw_py_importable_runfile.bzl", "pw_py_importable_runfile")
load("//pw_build:python.bzl", "pw_py_binary", "pw_py_test")

package(default_visibility = ["//visibility:public"])

licenses(["notice"])

py_library(
    name = "pw_presubmit",
    srcs = [
        "pw_presubmit/__init__.py",
        "pw_presubmit/bazel_checks.py",
        "pw_presubmit/bazel_parser.py",
        "pw_presubmit/block_submission.py",
        "pw_presubmit/build.py",
        "pw_presubmit/cli.py",
        "pw_presubmit/cpp_checks.py",
        "pw_presubmit/format_code.py",
        "pw_presubmit/git_repo.py",
        "pw_presubmit/gitmodules.py",
        "pw_presubmit/inclusive_language.py",
        "pw_presubmit/install_hook.py",
        "pw_presubmit/javascript_checks.py",
        "pw_presubmit/json_check.py",
        "pw_presubmit/keep_sorted.py",
        "pw_presubmit/module_owners.py",
        "pw_presubmit/ninja_parser.py",
        "pw_presubmit/npm_presubmit.py",
        "pw_presubmit/owners_checks.py",
        "pw_presubmit/pigweed_local_presubmit.py",
        "pw_presubmit/pigweed_presubmit.py",
        "pw_presubmit/presubmit.py",
        "pw_presubmit/presubmit_context.py",
        "pw_presubmit/pw_internal_namespace.py",
        "pw_presubmit/python_checks.py",
        "pw_presubmit/repo.py",
        "pw_presubmit/shell_checks.py",
        "pw_presubmit/source_in_build.py",
        "pw_presubmit/todo_check.py",
        "pw_presubmit/tools.py",
        "pw_presubmit/upstream_checks.py",
    ],
    imports = ["."],
    # TODO: b/433321728 - Imports private parts of pw_env_setup.
    tags = [
        "nomypy",
        "nopylint",
    ],
    deps = [
        ":format_core",
        "//pw_build/py:pw_build",
        "//pw_cli/py:pw_cli",
        "//pw_config_loader/py:pw_config_loader",
        "//pw_env_setup/py:pw_env_setup",
        "//pw_package/py:pw_package",
        "@rules_python//python/runfiles",
    ],
)

py_library(
    name = "format_core",
    srcs = [
        "pw_presubmit/format/__init__.py",
        "pw_presubmit/format/bazel.py",
        "pw_presubmit/format/cmake.py",
        "pw_presubmit/format/core.py",
        "pw_presubmit/format/cpp.py",
        "pw_presubmit/format/css.py",
        "pw_presubmit/format/gn.py",
        "pw_presubmit/format/go.py",
        "pw_presubmit/format/java.py",
        "pw_presubmit/format/javascript.py",
        "pw_presubmit/format/json.py",
        "pw_presubmit/format/markdown.py",
        "pw_presubmit/format/owners.py",
        "pw_presubmit/format/prettier.py",
        "pw_presubmit/format/private/__init__.py",
        "pw_presubmit/format/private/cli.py",
        "pw_presubmit/format/private/cli_support.py",
        "pw_presubmit/format/protobuf.py",
        "pw_presubmit/format/python.py",
        "pw_presubmit/format/rst.py",
        "pw_presubmit/format/rust.py",
        "pw_presubmit/format/starlark.py",
        "pw_presubmit/format/typescript.py",
        "pw_presubmit/format/whitespace.py",
    ],
    imports = ["."],
    visibility = ["//visibility:private"],
    deps = [
        "//pw_cli/py:pw_cli",
        "//pw_config_loader/py:pw_config_loader",
    ],
)

py_console_script_binary(
    name = "black",
    pkg = "@pigweed_python_packages//black",
    tags = [
        # TODO: https://github.com/bazel-contrib/rules_python/issues/3126 - Remove
        # this tag once the bug in rules_python is fixed.
        "nomypy",
        "manual",
    ],
    visibility = ["//visibility:private"],
)

pw_py_importable_runfile(
    name = "black_runfiles",
    src = ":black",
    executable = True,
    tags = ["manual"],
    visibility = ["//visibility:private"],
)

pw_py_importable_runfile(
    name = "buildifier_runfiles",
    src = "@buildifier",
    executable = True,
    tags = ["manual"],
    visibility = ["//visibility:private"],
)

pw_py_importable_runfile(
    name = "gofmt_runfiles",
    src = "@go//:bin/gofmt",
    executable = True,
    tags = ["manual"],
    visibility = ["//visibility:private"],
)

pw_py_importable_runfile(
    name = "gn_runfiles",
    src = "@gn",
    executable = True,
    tags = ["manual"],
    visibility = ["//visibility:private"],
)

pw_py_importable_runfile(
    name = "prettier_runfiles",
    src = "//:prettier",
    executable = True,
    tags = ["manual"],
    visibility = ["//visibility:private"],
)

pw_py_importable_runfile(
    name = "rustfmt_runfiles",
    src = "@rules_rust//tools/upstream_wrapper:rustfmt",
    executable = True,
    tags = ["manual"],
    target_compatible_with = select({
        "@platforms//os:none": ["@platforms//:incompatible"],
        "//conditions:default": [],
    }),
    visibility = ["//visibility:private"],
)

bool_flag(
    name = "enable_python_formatter",
    build_setting_default = True,
)

bool_flag(
    name = "enable_bazel_formatter",
    build_setting_default = True,
)

bool_flag(
    name = "enable_cpp_formatter",
    build_setting_default = True,
)

bool_flag(
    name = "enable_cmake_formatter",
    build_setting_default = True,
)

bool_flag(
    name = "enable_css_formatter",
    build_setting_default = True,
)

bool_flag(
    name = "enable_gn_formatter",
    build_setting_default = False,
)

bool_flag(
    name = "enable_go_formatter",
    build_setting_default = False,
)

bool_flag(
    name = "enable_java_formatter",
    build_setting_default = True,
)

bool_flag(
    name = "enable_javascript_formatter",
    build_setting_default = False,
)

bool_flag(
    name = "enable_json_formatter",
    build_setting_default = True,
)

bool_flag(
    name = "enable_markdown_formatter",
    build_setting_default = True,
)

bool_flag(
    name = "enable_owners_formatter",
    build_setting_default = True,
)

bool_flag(
    name = "enable_protobuf_formatter",
    build_setting_default = True,
)

bool_flag(
    name = "enable_rst_formatter",
    build_setting_default = True,
)

bool_flag(
    name = "enable_rust_formatter",
    build_setting_default = False,
)

bool_flag(
    name = "enable_starlark_formatter",
    build_setting_default = True,
)

bool_flag(
    name = "enable_typescript_formatter",
    build_setting_default = False,
)

config_setting(
    name = "python_formatter_is_enabled",
    flag_values = {":enable_python_formatter": "True"},
)

config_setting(
    name = "bazel_formatter_is_enabled",
    flag_values = {":enable_bazel_formatter": "True"},
)

config_setting(
    name = "cpp_formatter_is_enabled",
    flag_values = {":enable_cpp_formatter": "True"},
)

config_setting(
    name = "cmake_formatter_is_enabled",
    flag_values = {":enable_cmake_formatter": "True"},
)

config_setting(
    name = "css_formatter_is_enabled",
    flag_values = {":enable_css_formatter": "True"},
)

config_setting(
    name = "gn_formatter_is_enabled",
    flag_values = {":enable_gn_formatter": "True"},
)

config_setting(
    name = "go_formatter_is_enabled",
    flag_values = {":enable_go_formatter": "True"},
)

config_setting(
    name = "java_formatter_is_enabled",
    flag_values = {":enable_java_formatter": "True"},
)

config_setting(
    name = "javascript_formatter_is_enabled",
    flag_values = {":enable_javascript_formatter": "True"},
)

config_setting(
    name = "json_formatter_is_enabled",
    flag_values = {":enable_json_formatter": "True"},
)

config_setting(
    name = "markdown_formatter_is_enabled",
    flag_values = {":enable_markdown_formatter": "True"},
)

config_setting(
    name = "owners_formatter_is_enabled",
    flag_values = {":enable_owners_formatter": "True"},
)

config_setting(
    name = "protobuf_formatter_is_enabled",
    flag_values = {":enable_protobuf_formatter": "True"},
)

config_setting(
    name = "rst_formatter_is_enabled",
    flag_values = {":enable_rst_formatter": "True"},
)

config_setting(
    name = "rust_formatter_is_enabled",
    flag_values = {":enable_rust_formatter": "True"},
)

config_setting(
    name = "starlark_formatter_is_enabled",
    flag_values = {":enable_starlark_formatter": "True"},
)

config_setting(
    name = "typescript_formatter_is_enabled",
    flag_values = {":enable_typescript_formatter": "True"},
)

# This file is loaded by the formatter entry point at runtime and disables
# formatters by their mnemonic.
write_file(
    name = "disabled_formatters",
    out = "pigweed_format/disabled_formatters.txt",
    content = select({
        ":python_formatter_is_enabled": [],
        "//conditions:default": ["Python"],
    }) + select({
        ":bazel_formatter_is_enabled": [],
        "//conditions:default": ["Bazel"],
    }) + select({
        ":cpp_formatter_is_enabled": [],
        "//conditions:default": ["C/C++"],
    }) + select({
        ":cmake_formatter_is_enabled": [],
        "//conditions:default": ["CMake"],
    }) + select({
        ":css_formatter_is_enabled": [],
        "//conditions:default": ["CSS"],
    }) + select({
        ":gn_formatter_is_enabled": [],
        "//conditions:default": ["GN"],
    }) + select({
        ":go_formatter_is_enabled": [],
        "//conditions:default": ["Go"],
    }) + select({
        ":java_formatter_is_enabled": [],
        "//conditions:default": ["Java"],
    }) + select({
        ":javascript_formatter_is_enabled": [],
        "//conditions:default": ["JavaScript"],
    }) + select({
        ":json_formatter_is_enabled": [],
        "//conditions:default": ["JSON"],
    }) + select({
        ":markdown_formatter_is_enabled": [],
        "//conditions:default": ["Markdown"],
    }) + select({
        ":owners_formatter_is_enabled": [],
        "//conditions:default": ["Owners"],
    }) + select({
        ":protobuf_formatter_is_enabled": [],
        "//conditions:default": ["Protobuf"],
    }) + select({
        ":rst_formatter_is_enabled": [],
        "//conditions:default": ["reStructuredText"],
    }) + select({
        ":rust_formatter_is_enabled": [],
        "//conditions:default": ["Rust"],
    }) + select({
        ":starlark_formatter_is_enabled": [],
        "//conditions:default": ["Starlark"],
    }) + select({
        ":typescript_formatter_is_enabled": [],
        "//conditions:default": ["TypeScript"],
    }),
    visibility = ["//visibility:private"],
)

py_library(
    name = "formatters",
    srcs = ["pw_presubmit/format/formatters.py"],
    data = [
        ":disabled_formatters",
    ],
    deps = [
        ":format_core",
        "//pw_build/py:pw_build",
    ] + select({
        ":python_formatter_is_enabled": [":black_runfiles"],
        "//conditions:default": [],
    }) + select({
        ":bazel_formatter_is_enabled": [":buildifier_runfiles"],
        ":starlark_formatter_is_enabled": [":buildifier_runfiles"],
        "//conditions:default": [],
    }) + select({
        ":cpp_formatter_is_enabled": ["@llvm_toolchain//:py_clang_format"],
        ":java_formatter_is_enabled": ["@llvm_toolchain//:py_clang_format"],
        ":protobuf_formatter_is_enabled": ["@llvm_toolchain//:py_clang_format"],
        "//conditions:default": [],
    }) + select({
        ":gn_formatter_is_enabled": [":gn_runfiles"],
        "//conditions:default": [],
    }) + select({
        ":go_formatter_is_enabled": [":gofmt_runfiles"],
        "//conditions:default": [],
    }) + select({
        ":javascript_formatter_is_enabled": [":prettier_runfiles"],
        ":typescript_formatter_is_enabled": [":prettier_runfiles"],
        "//conditions:default": [],
    }) + select({
        ":rust_formatter_is_enabled": [":rustfmt_runfiles"],
        "//conditions:default": [],
    }),
)

pw_py_binary(
    name = "format",
    srcs = ["pigweed_format/__main__.py"],
    main = "pigweed_format/__main__.py",
    deps = [
        ":formatters",
        "//pw_build/py:pw_build",
        "@rules_python//python/runfiles",
    ],
)

pw_py_binary(
    name = "local_presubmit",
    srcs = ["pw_presubmit/pigweed_local_presubmit.py"],
    main = "pw_presubmit/pigweed_local_presubmit.py",
    deps = [":pw_presubmit"],
)

pw_py_test(
    name = "bazel_parser_test",
    srcs = ["bazel_parser_test.py"],
    deps = [
        ":pw_presubmit",
    ],
)

py_library(
    name = "format_testing_utils",
    srcs = ["format_testing_utils.py"],
    deps = ["//pw_cli/py:pw_cli"],
)

pw_py_test(
    name = "block_submission_test",
    srcs = ["block_submission_test.py"],
    deps = [":pw_presubmit"],
)

pw_py_test(
    name = "bazel_checks_test",
    srcs = ["bazel_checks_test.py"],
    deps = [":pw_presubmit"],
)

pw_py_test(
    name = "bazel_format_test",
    srcs = ["bazel_format_test.py"],
    data = [
        "pw_presubmit/format/test_data/bazel_test_data.bazel",
        "pw_presubmit/format/test_data/bazel_test_data_golden.bazel",
        "pw_presubmit/format/test_data/malformed_file.txt",
    ],
    deps = [
        ":buildifier_runfiles",
        ":format_testing_utils",
        ":pw_presubmit",
        "//pw_build/py:pw_build",
    ],
)

pw_py_test(
    name = "context_test",
    srcs = ["context_test.py"],
    deps = [
        ":pw_presubmit",
    ],
)

pw_py_test(
    name = "cpp_checks_test",
    srcs = ["cpp_checks_test.py"],
    deps = [
        ":pw_presubmit",
    ],
)

pw_py_test(
    name = "cpp_format_test",
    srcs = ["cpp_format_test.py"],
    data = [
        "pw_presubmit/format/test_data/clang_format_config",
        "pw_presubmit/format/test_data/cpp_test_data.cc",
        "pw_presubmit/format/test_data/cpp_test_data_golden.cc",
    ],
    deps = [
        ":format_testing_utils",
        ":pw_presubmit",
        "//pw_build/py:pw_build",
        "@llvm_toolchain//:py_clang_format",
    ],
)

pw_py_test(
    name = "format_core_test",
    srcs = ["format_core_test.py"],
    deps = [
        ":pw_presubmit",
    ],
)

pw_py_test(
    name = "gitmodules_test",
    srcs = ["gitmodules_test.py"],
    # TODO: b/314139942 - Requires git on the path.
    tags = ["manual"],
    deps = [
        ":pw_presubmit",
    ],
)

pw_py_test(
    name = "gn_format_test",
    srcs = ["gn_format_test.py"],
    data = [
        "pw_presubmit/format/test_data/gn_test_data.gn",
        "pw_presubmit/format/test_data/gn_test_data_golden.gn",
        "pw_presubmit/format/test_data/malformed_file.txt",
    ],
    deps = [
        ":format_testing_utils",
        ":gn_runfiles",
        ":pw_presubmit",
        "//pw_build/py:pw_build",
    ],
)

pw_py_test(
    name = "inclusive_language_test",
    srcs = ["inclusive_language_test.py"],
    deps = [
        ":pw_presubmit",
    ],
)

pw_py_test(
    name = "keep_sorted_test",
    srcs = ["keep_sorted_test.py"],
    deps = [
        ":pw_presubmit",
    ],
)

pw_py_test(
    name = "ninja_parser_test",
    srcs = ["ninja_parser_test.py"],
    deps = [
        ":pw_presubmit",
    ],
)

pw_py_test(
    name = "presubmit_test",
    srcs = ["presubmit_test.py"],
    deps = [
        ":pw_presubmit",
    ],
)

pw_py_test(
    name = "python_format_test",
    srcs = ["python_format_test.py"],
    data = [
        "pw_presubmit/format/test_data/black_config.toml",
        "pw_presubmit/format/test_data/malformed_file.txt",
        "pw_presubmit/format/test_data/python_test_data.py",
        "pw_presubmit/format/test_data/python_test_data_golden.py",
    ],
    deps = [
        ":black_runfiles",
        ":format_testing_utils",
        ":pw_presubmit",
        "//pw_build/py:pw_build",
        "@pigweed_python_packages//black",
    ],
)

pw_py_test(
    name = "rust_format_test",
    srcs = ["rust_format_test.py"],
    data = [
        "pw_presubmit/format/test_data/malformed_file.txt",
        "pw_presubmit/format/test_data/rust_test_data.rs",
        "pw_presubmit/format/test_data/rust_test_data_golden.rs",
        "pw_presubmit/format/test_data/rustfmt.toml",
    ],
    deps = [
        ":format_testing_utils",
        ":pw_presubmit",
        ":rustfmt_runfiles",
        "//pw_build/py:pw_build",
    ],
)

pw_py_test(
    name = "whitespace_format_test",
    srcs = ["whitespace_format_test.py"],
    deps = [
        ":format_core",
    ],
)

pw_py_test(
    name = "json_format_test",
    srcs = ["json_format_test.py"],
    data = [
        "pw_presubmit/format/test_data/json_test_data.json",
        "pw_presubmit/format/test_data/json_test_data_golden.json",
        "pw_presubmit/format/test_data/malformed_file.txt",
    ],
    deps = [
        ":format_core",
    ],
)

pw_py_test(
    name = "rst_format_test",
    srcs = ["rst_format_test.py"],
    data = [
        "pw_presubmit/format/test_data/rst_test_data.rst",
        "pw_presubmit/format/test_data/rst_test_data_golden.rst",
    ],
    deps = [
        ":format_core",
        ":format_testing_utils",
        "//pw_build/py:pw_build",
        "@llvm_toolchain//:py_clang_format",
    ],
)

pw_py_test(
    name = "owners_checks_test",
    srcs = ["owners_checks_test.py"],
    deps = [
        ":pw_presubmit",
    ],
)

pw_py_test(
    name = "todo_check_test",
    srcs = ["todo_check_test.py"],
    deps = [
        ":pw_presubmit",
    ],
)

pw_py_test(
    name = "tools_test",
    srcs = ["tools_test.py"],
    deps = [
        ":pw_presubmit",
    ],
)

# This can't be a native_test because tests run in the sandbox, and won't find
# any files.
native_binary(
    name = "check_formatting",
    src = ":format",
    args = ["--check"],
    tags = ["manual"],
)
